
<script>
import Vue from 'vue'
import mainTableFunction from './mixin/main-table-function'
import bottom_tip_item from "./bottom-tip-item.vue"
import super_input from "./super-input.vue"
import super_desc from "./super-desc.vue"
import confirm_button from "./confirm-button.vue"
import Sortable from 'sortablejs'
export default {
    name: 'super-table',
    mixins: [mainTableFunction],
    components: {
        bottom_tip_item, super_input, super_desc, confirm_button,
    },
    props: {
        authCheck: {
            type: Function,
            default: function (code) {
                if (this.$default_auth_check) {
                    return this.$default_auth_check(code)
                }
                return true
            },
        },

        defaultColAttr: { type: Object, default: () => ({ align: 'center', }), },
        data: {
            type: Array,
            default: function () {
                return [];
            }
        },

        model: Array,
        operate: Array,
        attrs: Object,
        operateColAttr: Object,
        specialStyle: {
            type: Boolean,
            default: true,
        },
        showIndex: {
            type: Boolean,
            default: true,
        },


        //ex_talbe
        pageChangeBefore: {
            type: Function,
            default: null,
        },
        defaultPageSize: {
            type: Number,
            default: 20
        },


        defaultSortColumn: {
            type: String,
            default: '_blank_'
        },

        simplePage: {
            type: Boolean,
            default: false
        },
        autoLoad: {
            type: Boolean,
            default: false
        },
        url: '',
        rowIndexField: {
            type: String,
            default: '_row_index_'
        },
        autoPage: {
            type: Boolean,
            default: false
        },
        autoFill: {
            type: Boolean,
            default: false
        },
        autoFlex: {
            type: Boolean,
            default: false
        },
        emptySlot: {
            type: Boolean,
            default: true
        },
        urlDataHandle: Function,//用于处理，url返回值非正常状态的情况
        urlParams: {
            type: [Object, Function],
            default: () => { return {} },
        },
        urlOption: {
            type: [Object],
            default: () => {
                return {
                    method: 'post'
                }
            },
        },
        tableVisible: {
            type: Boolean,
            default: true,
        },

        //原生el-table
        size: String,
        width: [String, Number],
        height: [String, Number],
        maxHeight: [String, Number],
        fit: {
            type: Boolean,
            default: true
        },
        stripe: Boolean,
        border: {
            type: Boolean,
            default: true
        },
        rowKey: [String, Function],
        context: {},
        showHeader: {
            type: Boolean,
            default: true
        },
        showSummary: Boolean,
        sumText: String,
        summaryMethod: Function,
        rowClassName: [String, Function],
        rowStyle: [Object, Function],
        cellClassName: [String, Function],
        cellStyle: [Object, Function],
        headerRowClassName: [String, Function],
        headerRowStyle: [Object, Function],
        headerCellClassName: [String, Function],
        headerCellStyle: [Object, Function],
        highlightCurrentRow: Boolean,
        currentRowKey: [String, Number],
        emptyText: String,
        expandRowKeys: Array,
        defaultExpandAll: Boolean,
        defaultSort: Object,
        tooltipEffect: String,
        spanMethod: Function,
        selectOnIndeterminate: {
            type: Boolean,
            default: true
        },
        indent: {
            type: Number,
            default: 16
        },
        treeProps: {
            type: Object,
            default() {
                return {
                    hasChildren: 'hasChildren',
                    children: 'children'
                };
            }
        },
        lazy: Boolean,
        load: Function,
    },

    render: function (h) {
        const _tableBind = this._tableBind()
        const props_attrs = {
            data: this.tableData,
            ..._tableBind
        }
        const componentType = Vue.component('ex-table-auto')
        const componentTypeProps = componentType.sealedOptions.props
        const props = _.pickBy(props_attrs, (value, key) => {
            return componentTypeProps[_.camelCase(key)] != undefined
        });
        const attrs = _.pickBy(props_attrs, (value, key) => {
            return componentTypeProps[_.camelCase(key)] == undefined
        });

        const columnKeys = this.model.filter(it => this._if(it)).map((it, i) => `key=${it.key}&type=${it.type}&i=${i}`)
        if (columnKeys.some(it => !this.tableColumnKeys.includes(it))) {
            this.$nextTick(() => {
                this.$refs.MainTable.doLayout();
                this.tableColumnKeys = columnKeys
            })
        }

        return h(componentType, {
            ref: 'MainTable',
            class: { 'main-table': true, 'main-table-flex': _tableBind.autoFlex, 'ex_talbe_special': this.specialStyle },
            props,
            attrs,
            on: this._onTable()
        }, [
            this.showIndex ? h('el-table-column', { props: { type: 'index', width: 50, label: '#', ...this.defaultColAttr } }) : null,
            this.$scopedSlots['table_before'] ? this.$scopedSlots['table_before']() : null,
            ...this.model.filter(it => this._if(it)).map((item, index) => this._renderColumn(h, item, index)),
            this.$scopedSlots['table_footer'] ? this.$scopedSlots['table_footer']() : null,
        ])
    },
    data() {
        return {
            tableColumnKeys: [],
            tableData: this.data,
        }
    },
    created() {
        if (this.operate) {
            console.error("!!##@@ operate will be de discard")
        }
    },
    mounted() {


    },

    watch: {
        data: function (val) {
            this.tableData = val
        },
    },

    methods: {

        _tableBind() {
            const defaultAttr = _.pick(this, [
                'height', 'size', 'width',
                'height', 'maxHeight', 'fit', 'stripe', 'border',
                'rowKey', 'context', 'showHeader',
                'showSummary', 'sumText', 'summaryMethod',
                'rowClassName', 'rowStyle', 'cellClassName',
                'cellStyle', 'headerRowClassName', 'headerRowStyle',
                'headerCellClassName', 'headerCellStyle', 'highlightCurrentRow',
                'currentRowKey', 'emptyText', 'expandRowKeys',
                'defaultExpandAll', 'defaultSort', 'tooltipEffect',
                'spanMethod', 'selectOnIndeterminate', 'indent',
                'treeProps', 'lazy', 'load',

                'pageChangeBefore', 'defaultPageSize',
                'defaultSortColumn', 'simplePage',
                'autoLoad', 'url',
                'rowIndexField', 'autoPage', 'autoFill', 'autoFlex',
                'emptySlot', 'urlDataHandle', 'urlParams',
                'urlOption', 'tableVisible',
            ])
            return Object.assign({}, defaultAttr, this.attrs, this.$attrs)
        },
        _renderColumn(h, item, index) {
            let key = "_col_" + index
            if (item.key) key = item.key + key
            if (item.type) key = item.type + key

            if (item.type == 'operate') {
                return this._renderColumnOperate(h, item)
            } else if (item.type == 'expandDesc') {
                return h('el-table-column', {
                    key,
                    props: Object.assign(this._propsColum(item),
                        { label: this.$tt(item.label || 'label_detail_expand'), type: 'expand' }),
                    scopedSlots: {
                        default: ({ row, $index }) => {
                            return h(super_desc, {
                                props: Object.assign(this._propsDesc(item.expandDesc), { data: row, })
                            })
                        }
                    },
                })
            } else {
                const is_auto_width = !(item.width || item['min-width'] || item.type)
                const scopedSlots = {}
                if (item.render) {
                    scopedSlots.default = ({ row, column, $index }) => {
                        return item.render(h, { row, column, index: $index, $index, list: this.tableData })
                    }
                } else if (item.component) {
                    scopedSlots.default = ({ row, column, $index }) => {
                        return h(bottom_tip_item, {
                            props: {
                                tip: row.error && row.error[item.key],
                            },
                            on: {
                                click: (e) => e.stopPropagation()
                            },
                        }, [
                            h(super_input, {
                                props: Object.assign({
                                    value: row[item.key],
                                }, item.component({ row, column, $index })),
                                on: {
                                    input: e => row[item.key] = e
                                }
                            })
                        ])
                    }
                } else if (item.slot && this.$scopedSlots[item.slot]) {
                    scopedSlots.default = ({ row, column, $index }) => {
                        return this.$scopedSlots[item.slot]({
                            model: item, row: row, column: column, $index,index: $index,
                        })
                    }
                }
                if (item.slot_header && this.$scopedSlots[item.slot_header]) {
                    scopedSlots.header = ({ column, $index }) => {
                        return this.$scopedSlots[item.slot_header]({
                            model: item, column: column, $index,index: $index,
                        })
                    }
                }
                let labelName = ""
                if (item.label instanceof Function) {
                    labelName = item.label(item, index)
                } else {
                    labelName = this.$tt(item.label)
                }
                const props = Object.assign(this._propsColum(item), {
                    prop: item.key,
                    label: labelName,
                    type: item.type,
                },
                    // is_auto_width ? { fontSize: 16, fontRate: { NUM_RATE: 0.5, OTHER_RATE: 0.55, CHAR_RATE: 1.1 } } : null
                )
                if (is_auto_width && this.tableData) {
                    let _minWidth = 100
                    const extra_auto_width = item.extra_auto_width || 6
                    this.tableData.forEach((it, i) => {
                        const _value = props.formatter ? props.formatter(it, props, it[item.key], i) : it[item.key]

                        if (typeof (_value) == 'string') {
                            const fills = _value.match(/[^\x00-\xff]/g)
                            const fillCount = fills ? fills.length : 0
                            const nowWidth = (((_value.length - fillCount) * 9) + ((fillCount) * 16)) + extra_auto_width
                            _minWidth = Math.max(_minWidth, nowWidth)
                        } else {

                        }
                    })
                    props['min-width'] = _minWidth
                }

                return h('el-table-column', {
                    key,
                    props,
                    scopedSlots,
                })
            }
        },
        _renderColumnOperate(h, item) {

            return h('el-table-column', {
                props: {
                    label: this.$tt(item.label || 'label_table_operate_area'),
                    fixed: "right",
                    ...this._propsColum(item),
                },
                scopedSlots: {
                    default: ({ row, $index }) => h('div', {
                        class: 'operate_div'
                    }, item.operate.map((itemInner, i) => {
                        return this._renderColumnOperateItem(h, itemInner, i, row, $index)
                    }).filter(it => it))
                }
            })
        },
        _authItem(item) {
            if (item.auth && this.authCheck) {
                return this.authCheck(item.auth)
            }
            return true
        },
        _renderColumnOperateItem(h, itemInner, i, row, $index) {
            if (!this._if(itemInner, row, $index)) return null

            let labelName = ""
            if (itemInner.label instanceof Function) {
                labelName = itemInner.label(row, $index)
            } else {
                labelName = this.$tt(itemInner.label)
            }
            if (itemInner.type == 'confirm-button' || itemInner.type == 'confirm_button') {
                if (!this._authItem(itemInner)) return null
                return h(confirm_button, {
                    ref: 'confirm_button',
                    refInFor: true,
                    key: `confirm-button.${$index}.${i}`,
                    props: {
                        label: labelName,
                        message: itemInner.message,
                        buttonClass: itemInner.buttonClass,
                        confirmButtonType: itemInner.confirmButtonType,
                        url: itemInner.url,
                        urlType: itemInner.urlType,

                        params: itemInner.params ? itemInner.params(row, $index) : null,
                        disabled: this._disabled(itemInner, row, $index)
                    },
                    on: {
                        show: this.confirmShow,
                        afterOperationDone: (res) => this.OperationDone(row, res, itemInner.done, $index, this.tableData,),
                    },

                })
            } else if (itemInner.type == 'more' || itemInner.type == 'list') {
                if (!this._authItem(itemInner)) return null
                const dropList = itemInner.items.filter(oitem => this._authItem(oitem))
                if (_.isEmpty(dropList)) return null
                return h('el-dropdown', {
                    props: {
                        trigger: 'click',
                        'hide-on-click': true,
                    },
                    on: {
                        command: (e) => dropList[e].click ? dropList[e].click(row, $index, this.tableData) : null
                    }
                }, [
                    h('el-button', {
                        props: {
                            type: 'text', size: 'mini',
                        },
                        on: {
                            click: (e) => e.stopPropagation()
                        },
                    }, [
                        labelName || this.$tt('label_more_operate'),
                        h('i', { class: 'el-icon-arrow-down el-icon--right' })
                    ]),
                    h('el-dropdown-menu', { slot: 'dropdown' }, dropList.map((it, ii) => {
                        if (!this._if(it, row, $index)) return null
                        let dropdown_labelName = ""
                        if (it.label instanceof Function) {
                            dropdown_labelName = it.label(row, $index)
                        } else {
                            dropdown_labelName = this.$tt(it.label)
                        }
                        return h('el-dropdown-item', {
                            key: ii, props: {
                                command: ii,
                                disabled: this._disabled(it, row, $index)
                            }
                        }, [
                            dropdown_labelName
                        ])
                    }).filter(it => it))
                ])
            } else {
                if (!this._authItem(itemInner)) return null
                return h('el-button', {
                    class: itemInner.class,
                    props: {
                        disabled: this._disabled(itemInner, row, $index),
                        ...Object.assign({ type: 'text', size: 'mini' }, itemInner.attrs,)
                    },
                    on: {
                        click: (e) => {
                            e.stopPropagation()
                            itemInner.click ? itemInner.click(row, $index, this.tableData) : null
                        }
                    }
                }, [
                    labelName
                ])
            }
        },
        _if(item, ...slotProp) {
            if (item.if == undefined) return true
            if (item.if instanceof Function) {
                return item.if(...slotProp)
            }
            return !!item.if
        },
        _disabled(item, ...slotProp) {
            if (item.disabled == undefined) return false
            if (item.disabled instanceof Function) {
                return item.disabled(...slotProp)
            }
            return !!item.disabled
        },
        confirmShow(e) {
            this.$refs.confirm_button.forEach(ref => {
                if (e != ref) ref.hide()
            })
        },
        gridDataChange(list) {
            this.tableData = list
        },
        setSortable(option) {
            const tbody = this.$refs.MainTable.$el.querySelector('.el-table__body-wrapper tbody ')
            let sortable_config = Object.assign({}, {
                setData: function () { },
                sort: true,
                delay: 0,
                animation: 300,
                onEnd: async (evt) => {
                    let list = this.tableData
                    list.splice(evt.newIndex, 0, list.splice(evt.oldIndex, 1)[0])
                    // this.tableData = list
                }
            }, option)
            Sortable.create(tbody, sortable_config)

        },
        _onTable() {
            const on = {
                'grid-data-change': this.gridDataChange
            }
            const selection = _.find(this.model, it => it.type == 'selection')
            if (selection && selection.clickToggle) {
                on['row-click'] = (row, column, event) => {
                    if (selection.clickExclude && selection.clickExclude.includes(column.property)) return
                    if (selection.selectable && !selection.selectable(row)) return
                    this.toggleRowSelection(row)
                }
            }
            this.assignFunction(on, this.$listeners)
            return on
        },
        assignFunction(object, sources) {
            return _.assignWith(object, sources, function (o, s) {
                const newFun = []
                if (o instanceof Array) {
                    newFun.push(...o)
                } else if (o) {
                    newFun.push(o)
                }
                if (s instanceof Array) {
                    newFun.push(...s)
                } else if (s) {
                    newFun.push(s)
                }
                return newFun
            })
        },


        _propsColum(item) {
            let { label, key, column, slot_header, if: _if, slot, propertyFilter, attrs, ...propsColum } = item

            const props = Object.assign({}, this.defaultColAttr, propsColum, attrs)
            if (propertyFilter) {
                props.filters = _.unionBy(this.tableData, key).map(it => ({ text: it[key], value: it[key] }))
                props['filter-method'] = (v, r, c) => {
                    return v == r[key]
                }
            } else if (props.filters instanceof Function) {
                props.filters = props.filters(this.tableData)
            }

            return props
        },
        _propsDesc(item) {
            let { model, data, attrs, ...desc } = item
            return { model, data, attrs: Object.assign({}, desc, attrs) }
        },
        OperationDone(row, res, item, i, data) {
            if (item) {
                if (item instanceof Array) {
                    item.map(it => {
                        this.OperationDone(row, res, it, i, data,)
                    })
                } else if (item instanceof Function) {

                    item(row, i, data, res,)
                }
                else {

                    this.Operation(item)
                }
            }

        },


        ////
        Operation(obj) {

            switch (obj.action || obj) {
                case 'reload': {
                    this.reload()
                    break
                }
                case 'tip': {
                    this.$message({
                        showClose: true,
                        message: obj.message || this.$tt('tip_operate_success'),
                        type: obj.type || 'success',
                    })
                    break
                }
            }

        },


    }
}
</script>
<style scoped>
.operate_div>*+* {
    margin-left: 10px;
}

.main-table-flex {
    flex: 1;
}
</style>
